Monthly Archives: May 2008

We Need More Growth of Nuclear Power

With this post, I’m beginning a new series or category of bog posts that I’m loosely terming “A Better Future.”


I’ve been thinking a lot about the grail of infinite power, coupled with the enormous rise in gas prices this week.

While I am all in favor of reducing wasteful consumption, increasing efficiency, and generally being smarter about everything, I do not believe we will ever reduce our energy requirements in the long-term. We are always inventing, always creating, and most things we create require power in some form. It’s a fool’s errand to try to reduce the actual energy we’ll use overall. This doesn’t even take into account all of the peoples of the world who are just now beginning to participate in the global economy. There will always be something to eat up the energy we produce. Fighting against this trend seems to me, in a way, trying to run evolution and progress backwards. Our race as a whole won’t do that. Given this, it makes much more sense to develop clean, efficient, abundant, cheap sources of energy.

Increasingly, I am convinced that the way to build out a vast network of nuclear reactors powering our grid. We have an enormous network of power distribution–we should be taking more advantage of it.

According to the US Department of DOE, our 103 active nuclear plants provide 20% of the nation’s electricity. You can even get the operational status of each one.

Worldwide, the IAEA predicts that the electric power generation capacity of the world in 2015 will be roughly 20,000 billion kilowatt hours. In that year, nuclear generation will provide roughly 2,972 billion kilowatt hours, or less than 15%. That report has a lot of other information and I highly encourage you to read it.

We need to increase that percentage drastically–to the point where it supplies power not just to homes, but to plug-in hybrid cars, and everything else.

Nuclear power has gotten a bad rap in the US and other parts of the world for a long time. I think the attitudes are changing, but not quickly enough. At what point will the benefits outweigh the risks in most minds? I think that point is almost upon us.

With the increasing development of pebble-bed reactors, nuclear technology is advancing. We need to increase this development to promote further advances in the safety and efficiency of these promising power sources. None of the operational reactors in the US are pebble-bed reactors (aka HTGR–high temperature gas-cooled reactors), nor are any planned. There is a research reactor at Idaho National Laboratory. All of the commercial HTGR development is taking place for other countries. These reactors, while not universally acclaimed, seem to be safer, cheaper, and the spent fuel less able to be repurposed as weapons-grade material.

We can’t wait for others to do these things–we need to do them. Our country needs to get in on the act at a higher level of commitment than ever. We can’t wait for these technologies to become perfected, either–that will happen over time. As we use a technology more, we will learn new techniques, ways to improve efficiency, and how to lower costs further.

There is no excuse for the US not  to be a leader in this area–we have one of the largest energy demands, the most capital, the most to gain by investing in it, and the most  to lose by not doing it.

The next generation of nuclear technology may not be the ultimate energy savior we’re looking for, but it’s a huge step in the right direction–a step we’ve delayed taking for too long.

Nuclear certainly has some down sides, but I’ll discuss those in a future entry.

Relevant Links:

  1. Pebble-bed reactors at wikipedia
  2. Energy Information Administration / Department of Energy
  3. International Atomic Energy Agency (IAEA)
  4. Inconvenient  Truths: Get Ready to Rethink What It Means to Be Green (Wired Magazine)
  5. Idaho National Laboratory
  6. Module Pebble Bed Reactor (MIT)

Virtual PC 2007 Tips and Tricks

 

This release of Virtual PC 2007 SP1 provides updates to existing features and introduces support for the following:

  • Windows Vista with Service Pack 1 (SP1) Business, Ultimate, and Enterprise operating systems as a host operating system

  • Windows Vista with SP1 Business, Ultimate, and Enterprise as a guest operating system

  • Windows Server 2008 Standard as a guest operating system

  • Windows XP with Service Pack 3 as both a guest and host operating system

  • Read The Virtual PC Guy’s Weblog
  • If you need, say, two virtual machines, one with Windows Vista and one with Windows Vista SP1, install Vista onto a new machine, then copy the machine files and install SP1 on one copy–much faster than installing Vista twice.
  • Need a screen shot of what’s in the virtual machine? Look at the Edit menu and choose “Select All” then “Copy”–a bitmap of the VM window will be on the clipboard. (This is how I got the capture of my BASIC graphics output from a previous post).

Custom data source for Google Earth

Using just these links as a guide, we quickly (less than a day) put up a data source over https for our customers to download GIS data to Google Earth.

KML reference and tutorial – KML is the XML language used to describe features that can be displayed in Google Earth and Google Maps.

Sample code to generate KML from a web service or web page (it’s VB.Net).

How to implement a custom authentication provider for IIS in .Net. Very useful if you need to authenticate your KML-generating web page and you don’t want to use Active Directory.

Formational Experiences

When I was 9, I started playing with GW-BASIC by typing in programs found in the old kid’s 3-2-1 Contact magazine. This soon progressed to QBASIC, where I mostly made cool graphics with lines and circles.qbasic_output

(click for larger image)

QBASIC is not included in Windows anymore, but you can still get it.

 qbasic_lines

(click for larger image)

I had also tried modifying the including GORILLAS.BAS and NIBBLES.BAS, but I was still a little too new at this.

When I was 14 I was getting into C/C++ via Borland C++ 3.1 in a big way and spent hours coding prank programs. I had two that I remember:

First was a program called Camels that displayed “I Love Camels!!!” in a vertical, colorful scrolling sine wave down the screen. It trapped Ctrl-Break/Ctrl-C so you couldn’t break out of it. If you hit Ctrl-K, it brought up a password screen that allowed you to exit if you knew the password. Then I put it on a lab at school, set AUTOEXEC.BAT to run it, and modified CONFIG.SYS with “switches /n” to disallow the user hitting F5 to skip processing of AUTOEXEC.BAT. This stunt kind of got me in trouble–the day after school ended, I got a call from my computer science teacher that he couldn’t access the computer and if I wanted a grade I had better get over there and remove that program because he couldn’t get onto the computer. So I had to bike a few miles to school (my parents were out of town) and remove it. Why didn’t the instructor just use a boot disk? No idea… By the way, I got an A.

One of my first Windows programs was something called “Chucky” (why? I don’t know…). Chucky liked to eat….hard disk space. He would startup with no Window, run in the background, and every few minutes it would add a few thousand lines of text to  the file C:\Windows\Chucky.txt. It was probably something like “I am Chucky, I am hungry.”

I even eventually convinced my parents to get me Turbo C++ so I could build Windows programs (suing OWL).

v6upWhen I was in college, I got Visual C++ 6 and thought a fun program would be a desktop utility that occasionally changed your Outlook signature to include a random quotation. You could build up a little database of quotes you liked, and the program would change it on a regular schedule. A friend of mine and I stayed up for nearly 3 days straight working on it. I did most of the programming–he was thinking of new ideas, ways to do things. It was great fun.

These important formational periods are what got me excited about programing. The learning that goes on during a 72-hour hacking session is something that can’t be duplicated in a classroom. The glee at creating pranks is not matched (often) by homework assignments. Sometimes when I’m feeling the drudgery of the current code I work on, I need to remember the excitement I felt back then.

I also need to find something equivalently exciting to work on. One of the things I’m going to do to “get the magic back” (so to speak) is to make sure I’m always experimenting with the latest and great .Net stuff coming out. I need to finally get into WPF, and I’ve even got a fun project to apply it to.

Lots of Good Movies This Year

We might have to make a few exception this year to our decision not to go the theater anymore…hopefully won’t have experiences like the last time

Tracking database changes using triggers

Tracking changes in database tables is an incredibly useful feature–especially for operational data that can change often. Having recently had to implement this feature, I thought I’d share some of the techniques I learned.

Sample Database

First, let’s conceptualize a very simple database consisting of user information (name, date of birth), and e-mails. A user can have more than one e-mail.

 

Table: UserData

Field Type
ID int (PK, identitiy)
FirstName varchar
LastName varchar
birthdate date

Table: UserEmails

Field Type
UserID int (FK)
email varchar

 

 

We want to track all changes to the FirstName, LastName, and birthdate fields. In addition we want to track when e-mails are added or removed from a user. As we’ll see, these aims are accomplished using two different methods.

My implementation is done in SQL Server 2000 and C#, but any database that supports triggers can be used.

Changes in a Single Table

With this method we want to track the changes to all fields of a table. In our example, we want to know when FirstName, LastName, and birthdate change values in the UserData table.

To accomplish this we need another table to track the history. This table is going to have the exact same fields as UserData, plus a few extra for the change tracking.

Table: UserDataChanges

Field Type
ChangeID int (PK, identity)
ChangeTime datetime
ChangeUser varchar
ID int (FK)
FirstName varchar
LastName varchar
birthdate date

Now the automated part–adding a trigger to populate this automatically:

CREATE TRIGGER UserDataChangeTrigger ON UserData FOR UPDATE, INSERT
AS    
    IF (UPDATE (FirstName) OR UPDATE(LastName) OR UPDATE(birthdate))    
    BEGIN     
        INSERT UserDataChanges 
            (ChangeTime, ChangeUser, ID, FirstName, LastName, birthdate)
            (SELECT GetUtcDate(), user, ID, FirstName,LastName,birthdate 
                FROM inserted)     
    END     

This trigger will insert a new row into the UserDataChanges table whenever a row in the UserData table is updated or inserted. The IF (UPDATE(FirstName)…. ) is not strictly required in this scenario, but in other cases I did not want a change recorded when certain fields were updated (i.e., you have a field that tracks the last change time of that row, or the number of orders, or any other field that can change frequently and isn’t important to track–you don’t want to create too much noise in this or it will not be useful). The GetUtcDate() and user are SQL Server functions that retrieve the current UTC time and the username of the process that caused the change–very useful for tracking responsibility. The inserted table is created by the server for use by the trigger and contains all the new values.

Changes in a Foreign Key Table

The UserEmails has to be handled differently because there can be multiple e-mails for each user and we can assume they can be added, or removed at will (Remove + Add = Update, so I won’t consider direct updates here).

The solution I landed on was to have a generic event log table that stores manual log entries as well as “special” entries denoting adding or removing e-mails.

Table: UserEventLog

Field Type
EventID int (PK, identity)
ID int (FK)
EventTime datetime
EventType int
ChangeUser varchar
Notes varchar

This table can be used for both adding text notes to a user and, by using the EventType field, special events. In our example, we have two events we need to track:

 

Event Value
EmailAdded 1
EmailRemoved 2

(In code, I’ve made these enumerations)

Next we add a trigger on the UserEmails table:

CREATE TRIGGER UserEmails_EmailAddedTrigger
ON UserEmails
FOR INSERT
AS
 BEGIN
     INSERT UserEventLog(ID, EventTime, EventType, ChangeUser, Notes)
        (SELECT ID, GetUtcDate(), 1, user, '{'+email+'}' FROM inserted)
 END

The value 1 stands for EmailAdded. I’ve added braces around the actual e-mail address to set it apart from regular notes (we’ll see how to integrate everything later).

To handle the deletion of e-mails add another trigger:

CREATE TRIGGER UserEmails_EmailRemovedTrigger
ON UserEmails
FOR DELETE
AS
 BEGIN
     INSERT UserEventLog(ID, EventTime, EventType, ChangeUser, Notes )
        (SELECT ID, GetUtcDate(), 2, user, '{'+email+'}' FROM deleted)
 END

The only things different: FOR DELETE (instead of INSERT), changed the EventType to 2 (EmailRemoved), and the values are taken from the SQL Server-supplied deleted table.

That’s enough to get a pretty good change-tracking system in place, but you’ll still have to build a UI to display it effectively.

Displaying the Changes in the UI

With the above work done, you end up with two types of entities: changes and events. While it would be possible to integrate all functionality into a single event/change table using a lot more logic in the SQL Trigger code, I’m personally more comfortable with the change logic being in my application code. I think this way the database is kept more “pure” and open to changes down the line.

That means we will need to integrate these two types of entities into a single list, ordered by date/time. I’m going to assume the existence of two classes or structs that represent each of these entities. They’ll be called UserChange and UserEvent. I’ll also assume that the lists of each of these are already sorted by time, since that’s trivial to do in a SQL query.

Given that, we need a function that takes both of these lists and produces a sorted, combined list with an easy-to-understand list.

How the function works:

  1. Go through both lists, and pick whichever one is next, time-wise.
  2. Translate the object into a string/list-view representation of that object.
  3. If it’s a UserChange object, compare it to the previous one to figure out what changed.
  4. Sort the list in reverse order to put newer items at the top.

Here’s the C# code which I’ve adapted from our production system. Don’t get hung up on the details:

 

private void FillLog(IList<UserEvent> events, IList<UserChange> changes)
{
    List<ListViewItem> tempItems = new List<ListViewItem>();
 
    int currentEventIdx = 0;
    int currentChangeIdx = 0;
    eventLogListView1.Items.Clear();
 
    while (currentEventIdx < events.Count
    || currentChangeIdx < changes.Count)
    {
    UserChange currentChange = null;
    UserChange prevChange = null;
    UserEvent currentEvent = null;
 
    DateTime changeTime = DateTime.MaxValue;
    DateTime eventTime = DateTime.MaxValue;
 
    if (currentChangeIdx < changes.Count)
    {
        currentChange = changes[currentChangeIdx];
        changeTime = currentChange.ChangeDate;
        if (currentChangeIdx > 0)
        {
        prevChange = changes[currentChangeIdx - 1];
        }
 
    }
 
    if (currentEventIdx < events.Count)
    {
        currentEvent = events[currentEventIdx];
        eventTime = currentEvent.EventDate;
    }
    string dateStr;
    string userStr;
    string eventTypeStr="";
    string notesStr;
 
    if (changeTime < eventTime)
    {
        dateStr = Utils.FormatDateTime(changeTime);
        userStr = currentChange.UserName;
        notesStr = GetChangeString(currentChange, prevChange);
        currentChangeIdx++;
    }
    else
    {
        dateStr = Utils.FormatDate(eventTime);
        userStr = currentEvent.UserName;
        notesStr = currentEvent.Notes;
        eventTypeStr = currentEvent.EventType.ToString();
        currentEventIdx++;
    }
 
    if (notesStr.Length > 0)
    {
        ListViewItem item = new ListViewItem(dateStr);
        item.SubItems.Add(userStr);
        item.SubItems.Add(eventTypeStr);
        item.SubItems.Add(notesStr);
        item.ToolTipText = notesStr;
        item.BackColor = (tempItems.Count % 2 == 0) ? 
            Color.Wheat : Color.White;
        tempItems.Add(item);
 
    }
 
    }//end while
    eventLogListView1.BeginUpdate();
    for (int i = tempItems.Count - 1; i >= 0; i--)
    {
    eventLogListView1.Items.Add(tempItems[i]);
    }
 
    eventLogListView1.AutoResizeColumn(0, 
        ColumnHeaderAutoResizeStyle.ColumnContent);
    eventLogListView1.AutoResizeColumn(1, 
        ColumnHeaderAutoResizeStyle.ColumnContent);
    eventLogListView1.AutoResizeColumn(2, 
        ColumnHeaderAutoResizeStyle.ColumnContent);
    eventLogListView1.Columns[3].Width = eventLogListView1.Width - 
    (eventLogListView1.Columns[0].Width +
    eventLogListView1.Columns[1].Width +
    eventLogListView1.Columns[2].Width +10);
 
    eventLogListView1.EndUpdate();
}

Now we need to define GetChangeString, which figures out the differences in successive UserChange objects and displays only pertinent information.

 

private string GetChangeString(
    BuoyDataChange currentChange, 
    BuoyDataChange prevChange)
{
    StringBuilder sb = new StringBuilder();
 
    if (prevChange == null)
    {
        CompareAndAdd(sb, "First Name", 
            null, currentChange.FirstName);
        CompareAndAdd(sb, "Last Name", 
            null, currentChange.LastName);
        CompareAndAdd(sb, "Birth Date", 
            null, currentChange.BirthDate);
    }
    else
    {
        CompareAndAdd(sb, "First Name", 
            prevChange.FirstName, currentChange.FirstName);
        CompareAndAdd(sb, "Last Name", 
            prevChange.LastName, currentChange.LastName);
        CompareAndAdd(sb, "Birth Date", 
            prevChange.BirthDate, currentChange.BirthDate);
    }
    return sb.ToString();
}

And one last helper function which compares two objects and if different appends the change to a StringBuilder object.

 

private void CompareAndAdd(StringBuilder sb, string field, 
    object oldVal, object newVal)
   {
       if (oldVal == null && newVal == null)
           return;
 
       if (oldVal == null || !oldVal.Equals(newVal))
       {
           if (sb.Length > 0)
           {
               sb.Append(", ");
           }
           sb.AppendFormat("{0}:{1} -> {2}", field, oldVal, newVal);
       }
   }

In this way you can end up with an automated system that displays all changes in an easy-to-understand format.

Here’s a sample of what our system looks like (click to enlarge):

Change log screenshot

Other ways to accomplish this? Better ways? Please leave a comment!

kick it on DotNetKicks.com

Opening Visual Studio solutions from Explorer in Vista

You’ve installed Visual Studio 2005 on Vista and dutifully changed it to run as administrator, like you’re supposed to. And then…

Problem: Visual Studio 2005 solutions no longer open when you double-click them in Windows Vista. In fact, when you double-click nothing happens.

Solution: Change them to open with Visual Studio 2005 directly instead of the vslauncher.exe (which opens up the solution with the correct version of Visual Studio if you have more than one).

Caveat: Only makes sense if  you use only Visual Studio 2005.

How-to:

  1. Right-click on a solution file.
  2. Choose “Open With…”
  3. Choose “Browse…”
  4. Browse to file “C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe” (or wherever you installed Visual Studio)
  5. Click “Open” button
  6. Check “Always use the selected program to open this kind of file”

openwith

Now your solutions will load Visual Studio, bring up the UAC prompt, and it all works great.

Found via here and here.

Software Pick: SyncMyCal

With the acquisition of a Blackberry I wanted to be able to sync both my work and home Outlook setups to the Blackberry (and to each other). I tried a number of free tools (though they aren’t that easy to find) and quickly concluded I would need a better solution.

Enter SyncMyCal. It’s easy, it’s cheap (only $25), and you can try for free. I rarely have duplicated events, and I don’t have to think about it ever–it just WORKS.

How it works: SyncMyCal synchronizes an Outlook calendar with a Google calendar. First I created a Google calendar, then I set up SyncMyCal on both work and home computers. I set the home computer to take priority in conflicts, but at work I set the Google calendar to take priority over Outlook–this way there’s a hierarchy of priority that helps to prevent unresolvable conflicts and duplicates.

I bought it days before Google released their Outlook sync tool, but SyncMyCal can do a lot more and I don’t regret the purchase one bit.

The latest version also syncs contacts, but I haven’t used that yet.

4 Principles of Not Wasting Time

There are so many postings out there on all sorts of blogs about how not to waste time that I’m not sure I can contribute something very meaningful (certainly not new), but since it’s something I’ve been thinking about, I might as well spill some ideas about it.

Like this? Please check out my latest book, Writing High-Performance .NET Code.

Definition

Any discussion of time-wasting is profitless unless you define what wasting time is. My definition is:

Wasting time is doing anything that does not contribute to my goals.

That is a very broad definition, but it is very useful. It presupposes a goal-oriented mind set and I don’t want to get too far down that path here. If you’re really interested in a goal-focused system, I highly encourage you to read The 7 Habits of Highly Effective People, which is a principles-based approach to effectiveness and goal-setting is a huge part of it.

Whatever your personal system, much of the corporate world and building software specifically revolves around goals (aka “milestones”, “targets”).

This definition includes taking breaks and eating lunch, but let’s not be silly–we’re not talking about that and I’m not going to water down the definition nor am I going to spend pages talking pointlessly about exceptions to it. We’re all intelligent people here and can understand the important principles.

Many Small Goals Are Better Than One Large One

Software development processes have been undergoing evolution since the beginning and lately the whole agile process seems to be taking over. Whatever the process, many companies are finding more success in breaking down large projects into tiny goal-driven chunks, sometimes lasting as little as a week.

This same principles can be applied to ourselves at both large and fine-grained levels. It is definitely good and desirable to have the overall vision of our project in mind, but  this doesn’t often help us get the work done. Some of my most productive days are when I break down a huge task into tiny subtasks and set a goal for each one (“I will have this done by 11am today, then I will wrap up this other small one by 4pm.”)

An example: I’m currently writing some code to move a huge amount of data around in our production database. We’re going to be rolling out a major update that requires some fundamental changes to how things operate. This task is so large and daunting that I get a headache just thinking about it and so I could put it off, just spinning my wheels until I decide to face the inevitable. Instead, I’ve broken it into several smaller tasks that are each easily managed and understood.

Before After
  • Convert database to new format
  • Export SQL script of new tables, triggers, indexes, etc. directly from SQL Server
  • Aggregate data from Table1 into NewTableX
  • Move data from Table2 to NewTableY
  • Move data from Table3 to NewTableZ
  • Verify moved data
  • drop old tables
  • drop old columns
  • etc.

(In my example, the After column actually contains about 30 items, depending on how far I want to break it down…it could be more.)

Now, instead of being overwhelmed by the complexity of a large task (and thus doing nothing), I can easily handle each of the sub-steps efficiently. I’ve changed a two-week task into many hour-long tasks.

The psychological effect of  too-hard/too-complex/too-much is devastating. You can’t handle something like that–no one can, and so you won’t–you’ll just end up wasting time fretting about it. Break it up for your sanity and happiness, as well as productivity.

Motivation is Crucial

Often, a key to not wasting time is having sufficient motivation. Motivation can come in all sorts of ways–the key is to figure out what motivates you and then set yourself up to succeed by using that motivation as a carrot to pull you forward. It can be a good idea to share your motivations with managers so they understand what drives you.

Motivation can often begin with picking good goals. If your goals are unrealistic, you are almost guaranteed to fail in some way. Despair feeds on itself and will sink your productivity and cause you to engage in anything but work. Not only will you avoid the drudgery of work, but you won’t take steps to improve yourself or change the situation. This cycle must be broken immediately.

If your projects are just not that interesting this can be a challenge. Everybody has tasks they don’t particularly like, but if the majority of your time is spent doing stuff you get no pleasure out of, you are doing a huge disservice to yourself and your future. Eventually, you’ll become wasted and useless to both yourself and your employer. Fix the situation–get a new project, get a new job, find side projects to do that you do enjoy as rewards for getting through the drudgery–anything to avoid becoming the shell of a person you once were.

Maybe you don’t necessarily need a new job right now–maybe you just need to fix the situation at your current job, get some enjoyable hobbies at home, spend more time with the family. The needed changes aren’t always drastic–but figure them out so you don’t spend every day wallowing in a mire not doing anything useful.

Eliminate Distractions Now

I don’t think I’ve answered my office phone in about a year. Not that many people call it in the first place, e-mail being highly-preferred around here, but I like to say I stand on principle. You can read a lot about creating the right environment for highly-skilled software developers in the fabulous book Peopleware: Productive Projects and Teams.

I’ve also stuck to the practice of keeping my e-mail inbox cleared. I delete almost everything I receive unless I need to store it for later or act on it.

In my entry about working an 8-hour day, I talked about various time-wasting activities that I’ve observed, such as  wandering the halls, micro-managing, too many words, poor inbox management skills, and more.

The goal of eliminating distractions is not to completely choke off any aspect of fun, diversion, and other social aspects of working in an office–those are good things. The goal is eliminate the things that don’t help us in our jobs and that aren’t really all that enjoyable to us anyway, all things considered.

One way of eliminating your distractions is to go out of your way ahead of time to manage them so they don’t come up later. Some ideas:

  • If you need somebody to do a task, anticipate questions, concerns, or problems they may have. Try to address these in an e-mail or in person (or both) quickly so that the person can expect it and won’t come to you later with problems.
  • Shut  the office door, turn off the phone, close e-mail. Don’t let people find a way to distract you.
  • Shut down your feed-reader, disable pop-up notifications from it. Set aside time during the day to review feeds and news.
  • Have a clean desk. Keep only things you’re actively working on visible.
  • Set a schedule or a signal to your peers and supervisors of times when you are busy and should not be bothered. Be assertive and enforce it.
  • Focus on one project at a time. Everybody has a million things to do.

Plan Weekly, Daily, Hourly

Finally, bringing it around full circle back to goals: plan as much as you can to the extent it makes sense. That’s a weasel sentence, I know, but there’s no way around it. In general, though, I think we could all do with more planning.

Effective planning combines all the above principles into a coherent framework for your work week.

Every week, you have certain meetings, tasks to be completed, issues to be researched, people to be spoken to.

Every day, a certain subset of those must be done.

Every hour, you must pick a task to work on.

My plan is to take 10-20 minutes every day to plan the day’s activities, set min-goals, while at the same time strategizing to eliminate distractions. Every Monday morning I take an additional 10-20 minutes to review and set the major goals for the week, ensure meetings are scheduled, projects are given the correct priority, I know my tasks and responsibilities, and that I have enough dead-time left unscheduled because things always come up (we operate quickly-growing 24/7/365 services–there’s no avoiding issues).

Every time you finish a task, there should be another one waiting, whether you decide to tackle it right away or take a break and do something else. As long as you have a plan, it’s ok.

There are tons of other resources out there–I’ll just link to some in the forums of Steve Pavlina.

Now I should stop wasting time and get back to work… 😉