Reflections on Pair Programming feat. ChatGPT

@codewiz · December 01, 2023 · 10 min read

There are several methodologies classified under Agile, such as Test-Driven Development, Scrum, and Pair Programming. I have my likes and dislikes among them, but I particularly despise Scrum and Pair Programming. If I were in a job interview and asked if I had any questions, I'd definitely ask about these practices. "Do you implement Scrum and Pair Programming? If so, are they mandatory?" If they are, I would not consider joining the company; that's how much I detest them. For now, let's set Scrum aside and focus solely on Pair Programming.

I personally believe that Pair Programming works best when both partners have similar intellectual abilities regarding the current task. If the gap is too wide, it resembles more of an apprenticeship or a lesson than actual Pair Programming. I'm aware of numerous studies suggesting otherwise, but research is research, and practice is practice. My real-world experience with Pair Programming has differed significantly from these findings.

So, does Pair Programming only work well when both pairs are equally skilled? Not necessarily, because we're human. And what does that mean? Humans perceive and react differently. Even the same situation can lead to vastly different outcomes depending on an individual's mood. To work harmoniously on a single monitor, the chemistry between the two needs to align perfectly.

So, what's the likelihood of finding a partner with similar intellectual abilities and perfect chemistry for Pair Programming in the real world? Without crunching the numbers, I feel it's more likely for me to win the lottery.

This might sound like the more knowledgeable person always ends up at a disadvantage. However, in my experience, Pair Programming often turns out to be a lose-lose situation. Skilled programmers feel they gain little and often find it inconvenient. Interestingly, programmers perceived as less skilled express similar sentiments. This is where Pair Programming diverges from apprenticeship. In an apprenticeship, at least the learner gains something.

For these reasons, I've always disliked Pair Programming. Well, until now, that is. Everything changed in February this year with the arrival of ChatGPT. This AI eradicates all the downsides of Pair Programming I've mentioned. Its intellectual ability is undoubtedly superior to mine, yet it remains unemotional and endlessly patient, no matter what nonsense I throw at it. Plus, it's ready to code with me 24/7, 365 days a year. Sure, sometimes the OpenAI server is down, but that's rare, and I can just take a break then.

After nearly 9 months of Pair Programming with ChatGPT, I've come to realize that without the aforementioned drawbacks, Pair Programming can indeed be effective. So, at my next job interview, if asked what I'm curious about, I might ask something like this: "Is the use of AI allowed? Do you support ChatGPT?"

Below is a part of my conversation with ChatGPT while writing JavaScript code to dynamically change table cell sizes in a blog. Looking back at it, this feels less like Pair Programming and more like hiring a coding slave. Nonetheless, it's a fascinating new world.

Pair Programming with ChatGPT, or rather, getting it to do the coding.

"I never imagined that one day I would be learning coding from an artificial intelligence."

"I never imagined that one day I would be learning coding from an artificial intelligence."

You
In my Gatsby blog markdown, there are three table cells, but the first two are too small and the last one is too big. How can I adjust them to be proportionally sized?

ChatGPT
To adjust the cell sizes in a Markdown table on your Gatsby blog, there are several methods you can try. Since Markdown itself does not have a direct feature to adjust cell sizes, the most common method is to apply styles using CSS. Here are some methods:

...[Summary]...

  • Using CSS
  • Applying Inline Styles
  • Utilizing JavaScript

...[Summary]...

You
Is there a way to dynamically set the table cell sizes based on the content inside them?

ChatGPT
To dynamically adjust table cell sizes based on their content, using JavaScript is the most effective approach. CSS and Markdown alone cannot adapt the size of cells in response to their content. With JavaScript, you can read the content of each cell and dynamically assign the appropriate size based on that content.

...[Summary]...

document.addEventListener("DOMContentLoaded", function() {
  const table = document.querySelector('.my-table');
  const rows = table.getElementsByTagName('tr');

  for (let row of rows) {
    const cells = row.getElementsByTagName('td');
    for (let cell of cells) {
      const contentLength = cell.textContent.length;

      if (contentLength < 10) {
        cell.style.width = '20%';
      } else if (contentLength < 20) {
        cell.style.width = '30%';
      } else {
        cell.style.width = '50%';
      }
    }
  }
});

You
Instead of dynamically setting it based on cell content, it might be better to find the longest length of each column and set the sizes based on that ratio. Can you modify it accordingly?

ChatGPT
To dynamically adjust the sizes of table cells, a good approach is to determine the sizes based on the longest content in each column. This can be implemented using JavaScript, and involves the following steps:

...[Summary]...

document.addEventListener("DOMContentLoaded", function() {
  const table = document.querySelector('.my-table');
  const rows = table.getElementsByTagName('tr');
  let maxContentLengths = [];
  let totalMaxLength = 0;

  for (let row of rows) {
    const cells = row.getElementsByTagName('td');
    cells.forEach((cell, index) => {
      const contentLength = cell.textContent.trim().length;
      maxContentLengths[index] = Math.max(maxContentLengths[index] || 0, contentLength);
    });
  }

  totalMaxLength = maxContentLengths.reduce((acc, length) => acc + length, 0);

  for (let row of rows) {
    const cells = row.getElementsByTagName('td');
    cells.forEach((cell, index) => {
      const widthPercentage = (maxContentLengths[index] / totalMaxLength) * 100;
      cell.style.width = `${widthPercentage}%`;
    });
  }
});

You
I'm getting an error that says 'cells.forEach is not a function'.

ChatGPT
The error 'cells.forEach is not a function' occurs because the HTMLCollection object returned by the getElementsByTagName method is not an Array, and therefore does not support the forEach method. To resolve this issue, you can use the Array.from method to convert the HTMLCollection into an array, or use a traditional for loop.

...[Summary]...

document.addEventListener("DOMContentLoaded", function() {
  const table = document.querySelector('.my-table');
  const rows = Array.from(table.getElementsByTagName('tr'));
  let maxContentLengths = [];
  let totalMaxLength = 0;

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));
    cells.forEach((cell, index) => {
      const contentLength = cell.textContent.trim().length;
      maxContentLengths[index] = Math.max(maxContentLengths[index] || 0, contentLength);
    });
  });

  totalMaxLength = maxContentLengths.reduce((acc, length) => acc + length, 0);

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));
    cells.forEach((cell, index) => {
      const widthPercentage = (maxContentLengths[index] / totalMaxLength) * 100;
      cell.style.width = `${widthPercentage}%`;
    });
  });
});

You
Add exception handling for when there are no tables at all.

ChatGPT
To add exception handling for cases where there are no tables, you need to first add logic to check for the presence of table elements. You can use a simple conditional statement that checks if the table element selected with document.querySelector is null. If no table exists, you can stop the execution of the function or display an appropriate message.

...[Summary]...

You
It seems necessary to also handle exceptions for when there are no tr or td elements in a table.

ChatGPT
Adding exception handling for cases where a table lacks tr or td elements is a good approach. For this, you need to add conditional statements to check if the array of tr elements is empty and whether each tr contains td elements. If there are no tr or td elements, you can stop the function's execution or display a warning message.

...[Summary]...

You
Is Array.from(null) defined?

ChatGPT
Array.from(null) is not defined. The Array.from method is used to create a new array instance from an array-like or iterable object. However, null is not an iterable object, so using Array.from(null) will result in an error.

Therefore, it's essential to ensure that the object is not null before using Array.from. For instance, when DOM methods like document.getElementsByTagName or document.querySelectorAll return an empty NodeList, you should check for null before converting it to an array.

You
Modify it accordingly and write a new function.

ChatGPT
Based on our discussion, I will write a new JavaScript function that includes exception handling for cases where a table has no tr or td elements. This function will only execute the cell size adjustment logic if there is a table and it contains at least one tr and td.

...[Summary]...

You
Modify the code to ensure that each cell is at least 15% wide, as cells with too low a percentage can be problematic.

ChatGPT
I'll add logic to ensure that the width of each cell is at least 15%, preventing the cell width ratios from becoming too low. This involves checking each cell's calculated ratio and adjusting it to 15% if it falls below this threshold. Additionally, you must recalculate the total width to ensure it does not exceed 100% following these adjustments.

Here is the modified code:

document.addEventListener("DOMContentLoaded", function() {
  const table = document.querySelector('.my-table');

  if (!table) {
    return;
  }

  const rows = Array.from(table.getElementsByTagName('tr'));

  if (rows.length === 0) {
    return;
  }

  let maxContentLengths = [];
  let totalMaxLength = 0;
  let isCellPresent = false;

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));

    if (cells.length > 0) {
      isCellPresent = true;
    }

    cells.forEach((cell, index) => {
      const contentLength = cell.textContent.trim().length;
      maxContentLengths[index] = Math.max(maxContentLengths[index] || 0, contentLength);
    });
  });

  if (!isCellPresent) {
    return;
  }

  totalMaxLength = maxContentLengths.reduce((acc, length) => acc + length, 0);

  let widthPercentages = maxContentLengths.map(length => (length / totalMaxLength) * 100);
  let adjustedPercentages = widthPercentages.map(width => Math.max(width, 15));
  let adjustedTotal = adjustedPercentages.reduce((acc, width) => acc + width, 0);

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));
    cells.forEach((cell, index) => {
      const adjustedWidth = (adjustedPercentages[index] / adjustedTotal) * 100;
      cell.style.width = `${adjustedWidth}%`;
    });
  });
});

You
But if I make the adjustments like that, for example, if the columns are in a 5, 5, 90 ratio and then get adjusted to 15, 15, 90, wouldn't the first column ultimately be set to less than the requested 15% because it becomes 15 / 120, which is 12.5%?

ChatGPT
Yes, you're correct. If the adjusted ratio changes to 15%, 15%, 90%, the total exceeds 100%, and when recalculated, the intended minimum of 15% for each column may not be maintained. To resolve this, additional handling is needed when the sum of the adjusted ratios exceeds 100%.

The process of adjusting the ratios to ensure each column is at least 15% while keeping the total under 100% could be:

First, calculate the ratio for each column. Adjust any column below 15% to 15%. If the total exceeds 100% after adjustment, reduce the ratios of columns above 15% to ensure the total equals 100%. This process can be complex, especially considering various scenarios depending on the number of columns and their ratios. A simple solution could be to set fixed ratios ensuring each column is at least 15%, or implementing a more complex algorithm to dynamically adjust the ratios.

You
Don't just explain, write the code for it.

ChatGPT

...[Summary]...

document.addEventListener("DOMContentLoaded", function() {
  const table = document.querySelector('.my-table');

  if (!table) {
    return;
  }

  const rows = Array.from(table.getElementsByTagName('tr'));

  if (rows.length === 0) {
    return;
  }

  let maxContentLengths = [];
  let totalMaxLength = 0;
  let isCellPresent = false;

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));

    if (cells.length > 0) {
      isCellPresent = true;
    }

    cells.forEach((cell, index) => {
      const contentLength = cell.textContent.trim().length;
      maxContentLengths[index] = Math.max(maxContentLengths[index] || 0, contentLength);
    });
  });

  if (!isCellPresent) {
    return;
  }

  totalMaxLength = maxContentLengths.reduce((acc, length) => acc + length, 0);

  let widthPercentages = maxContentLengths.map(length => (length / totalMaxLength) * 100);
  let adjustedPercentages = widthPercentages.map(width => Math.max(width, 15));
  let adjustedTotal = adjustedPercentages.reduce((acc, width) => acc + width, 0);

  if (adjustedTotal > 100) {
    let excess = adjustedTotal - 100;
    adjustedPercentages = adjustedPercentages.map(width => {
      if (width > 15 && excess > 0) {
        let decrease = Math.min(width - 15, excess);
        excess -= decrease;
        return width - decrease;
      }
      return width;
    });
  }

  rows.forEach(row => {
    const cells = Array.from(row.getElementsByTagName('td'));
    cells.forEach((cell, index) => {
      cell.style.width = `${adjustedPercentages[index]}%`;
    });
  });
});
@codewiz
Looking back, there were good days and bad days. I record all of my little everyday experiences and learnings here. Everything written here is from my personal perspective and opinion, and it has absolutely nothing to do with the organization I am a part of.
(C) 2001 YoungJin Shin, 0일째 운영 중