Technical Debt and Handling Upgrades

In pretty much any project of a reasonable size, you are going to change data formats over time. As a result, you have hidden technical debt. What happens to the data on the local clients when the data format changes?

I had that situation recently on my own browser with my HoneyDo application. There were four specific problems I had:

  1. Originally, I had stored a serialized list of tasks in a single entry in the localforage storage.
  2. I had introduced a bug that duplicated the initial set of tasks at each browser refresh with the same ID
  3. I had introduced a bug that duplicated the initial set of tasks at each browser refresh with a different ID but the same titles, due dates and completed dates
  4. When I save the tasks, I don’t clean out the deleted task which causes them to be reloaded at refresh

There is obviously fixing the bugs – that’s the easy part. The technical debt is in cleaning up the store so that the user doesn’t have to continually fix their task list. Three of these cases are handled in my loadTasks() method inside the flux/TaskStore.js file and one of them is handled during saving in the saveTasks() method.

The fixed version of the loadTasks() method looks like this:

  loadTasks() {
    localforage.iterate((value, key, iterationNumber) => {
      let task = Task.deserialize(value);
    }).then(() => {
      if (this.tasks.length === 0) {
        console.log('We need to initialize');

12 lines of code – not too shabby.

Fixing the First Three Data Problems

I need to detect three specific conditions. In the first case, I need to add a piece of code right at the beginning of the loadTasks() method that looks for the old item and converts it using the old code. Once I’ve converted it, remove the old item so that I don’t re-run the conversion:

     * CLEAN UP - we used to store stuff in honeydo-tasks - we need to
     *  load the data from the key and then remove it.
      .then(v => {
        if (v !== null) {
'Old Data detected - handling upgrade');
          v.forEach(json => this.tasks.push(Task.deserialize(json)));

For the second and third cases, I need to detect duplicate data while I am iterating over it. I replaced the iterate() contents with this code:

      let task = Task.deserialize(value);
      // CLEAN UP: There was an old software case where duplicate IDs were
      // stored in the localforage store.
      let cleanup = false;
      let offset = this.tasks.findIndex(t => ===;
      if (offset !== -1) {'Duplicated ID detected - handling upgrade');
        cleanup = true;
      } else {
        // CLEAN UP: There was an old software case where duplicate tasks were
        // created with the SAME data but different IDs.
        offset = this.tasks.findIndex(t => {
          let title_check = t.title === task.title;
          let due_check = ((!t.due.isValid() && !task.due.isValid()) || t.due.isSame(task.due, 'day'));
          let comp_check = ((!t.completed.isValid() && !task.completed.isValid()) || t.completed.isSame(task.completed, 'day'));
          return title_check && due_check && comp_check;
        if (offset !== -1) {
'Duplicated data detected - handling upgrade');
          cleanup = true;

      // If we didn't need to clean it up, then add it to the list
      if (!cleanup) {

I added 39 lines of code – 3 times as much as the original code – just to handle the special cases caused by my bugs.

Guess what – this is common. Software projects grow over time and you end up having to deal with all the messy bugs that you created and the effects on released code. Normally, that code just sits there gathering dust, long after the reason for the code has been forgotten. This is technical debt – I need to, at some point in the future, clean up this code so that it isn’t just gathering maintenance dust.

There are other reasons for technical debt in software projects. The most notable one starts with a statement like “I know this isn’t the most efficient way to do this – it’s using an undocumented feature or it doesn’t handle a specific corner case properly – but it’s good enough for now so that we can get to release”. Then you never get to tidy it up.

In my case, the only question is how long do I leave that code there? I’m thinking the right answer is “through the next release” since it’s a web site. It’s always a good idea to understand when you can remove extra code.

So, here is the short version:

  • Use a bug tracking system – GitHub Issues is good enough
  • Identify Technical Debt early and file bugs so you don’t forget
  • Identify when you need to fix the technical debt and put it in the bug

Don’t let technical debt linger.

One thought

  1. Hi Adrian,

    Nice article. I especially like your point about ‘Fixup’ code added once a application is running. Keeping your data clean seems to be a never ending problem. The worst for me is database changes on live systems!

    I find noting each Technical Debt down is really important. I use Trello, , for my projects and normally have with a column just of tech debt. Sometimes when I am adding/refactoring a section of code its fairly easy to sort out a Technical Debt, as long as I remember what it was and what I thought I needed to do. So adding a comment inside a tech debt Trello card when the problem is fresh in my mind has saved me a lot of time later when I am working in the same area.

    Thanks again.

    Jon Smith


Comments are closed.