Recruiter Codes Chrome Extension pt.3 —Another ChatGPT fail story!

Finally reaching the end of this 12–and now-3-part series! Or am I..?

Thanks to the great learning opportunities at FusionWorks, even a lowly tech recruiter like me has lots of coding stories to tell, but the question is: are they entertaining for people other than the 2 or 3 inside my head? 😂

This time, thanks to the omnipresence of Chat-GPT in my (and many others’) workflow, I think I got a funny story, of which I’ll spoil the conclusion right away: developers, software engineers, or however else “coding people” self-define, are still not going to be replaced anytime soon! 🥳

DISCLAIMER: Like crypto-bros say “don’t take this as financial advice” I’ll say “don’t take this as coding advice”! I’m a tech recruiter, not a developer! 😁

A bit of context/recap!

Finally reaching the end of this 12–and now-3-part series! Or am I..?

Thanks to the great learning opportunities at FusionWorks, even a lowly tech recruiter like me has lots of coding stories to tell, but the question is: are they entertaining for people other than the 2 or 3 inside my head? 😂

This time, thanks to the omnipresence of Chat-GPT in my (and many others’) workflow, I think I got a funny story, of which I’ll spoil the conclusion right away: developers, software engineers, or however else “coding people” self-define, are still not going to be replaced anytime soon! 🥳

DISCLAIMER: Like crypto-bros say “don’t take this as financial advice” I’ll say “don’t take this as coding advice”! I’m a tech recruiter, not a developer! 😁

A bit of context/recap!

My Chrome Extension grew almost exponentially since last time we “spoke”, at least in my perception… For the world outside of my head, it’s still quite a simple app that scrapes data and sends it to a Google Apps Script web app I also wrote, which in turn writes it on a Google Sheet.

A well-thought-out extension would surely be able to do all this without the intermediary, but I prioritized speed, or better yet EASE of learning.

A rather accurate diagram of my Chrome Extension coupled with my Google App Script App! 😁

Still, when choosing easy paths, one sometimes ends up doing more work

For example, it’s not comfortable to maintain 3 different codebases for what is essentially a mass structured copy-pasting, but it’s also conceptually much easier than creating a monolithic app (and anyway the 3rd codebase, the scripted part of the destination Google Sheet, would not be avoided.)

A story within the story (⏩Skip if not into coding!)

New things included a massive refactoring to an asyncawait pattern I finally decided to learn, replacing nested callback functions which i used to like, and now see ugly as hell! 😂

This is also used to preload imageData for the waiting animation, and this part sparked quite a debate online, for my usage of forEach with async…

While not directly related to the main story, which has to do with CRUD operations on Google Sheets, coding-wise both issues have to do with how arrays work in Java/TypeScript: again, DO SKIP this if not into coding!

Notice also the fancy way of entering 10 similar filenames into ImgNames: hot or not? 🤔

The question about this code was: will Icons[] be ordered like ImgNames[]?Yes, because even if the promises all fulfill at different times, the array positions the data will fill in the array are explicitly referenced by index.

I get this is not considered good practice, and probably hard to test, but the thing is many more elegant solutions are forbidden in the context! 🤔

First of all, we can’t really draw image elements here, because this is the Service Worker part of the extension, so it has no page/DOM of its own.

This is another context in which ChatGPT could help very little, but I’ll digress…

Second, it can’t use modules or be a module of its own (a recent Chrome update has introduced the possibility, but requiring quite a few systemic change I didn’t yet want to commit to), so “top-level awaits” are not allowed.

An alternative is to use this (IMO ugly) thing called IIAFE (Immediately Invoked Asynchronous Function Expression) to go around the above limitation, by encapsulating the “await” while still executing it right away.

I tested both and didn’t notice any changes, but I’ll be welcoming comments on what you think about this coding debate! 🧑‍💻💭

The actual story: FAST remote I/O on Google Sheets!

Finally getting to the heart of the topic, the extension now (finally) works using the POST method to communicate with the Google Apps Script part, and that was actually a necessity, since now it can get 25 contacts at a time, and the URL would have gotten extremely big if sending data via GET…

What it does is filling in a custom contact type procedurally, in a typical double-loop fashion going through rows and columns of the table.

The way it manages this neatly is by giving the type definition the same keys as the column titles on the page. Plus a lot of typecasting to force things a bit, which might mean TypeScript is a bit wasted here? 🙄 Let me know!

I know it returns a Failed state and a string instead of an object, but I won’t get into why it does that! 😂

The receiving end is where the story lies. It divides incoming contacts into new or existing, adding or updating accordingly.
While this may seem the easiest thing in the world, doing separate I/O operations for each entry would (and did) take FOREVER, even if we’re talking only 25 contacts at a time.

This is due to the positioning of RichTextValues in relation to ones that can’t be RichText on the Google Sheet, and how GAS forces writes to be only in contiguous ranges. It meant 4 writes per contact, if done one by one! 🙈

But something you learn of Google Sheets scripting, is that it’s faster to rewrite THOUSANDS of rows in one go, than 10 rows in separate calls!

So to make sure these 25 updates would be done in a short amount of time, I realized I should have adapted to Google Sheets’ ways, the hard way:

  • Instead of having values that can’t be RichText in-between values that are, forcing me to differentiate the ranges to read and write the data, have all RichText values as close as possible (fortunately this meant just moving a hyperlink to another column, without changing the content.)
  • Instead of writing each line separately, process everything in the arrays, then write all lines, INCLUDING those not updated if they are in-between updated ones, so only 2 I/O operations are done respectively for adds and updates: 1 for normal and 1 for RichText.

This could lead to a total of 4 I/O operations from a previous total of 4 ops for all new plus 4 for each updated, so down from 52 on an average of half-new half-updated, or down from 100 if all needed to be updated! 🤯

Me when I realized this!!

Granted, the operations are bigger than before, especially if by chance the contacts to be updated are separated by thousands of rows, but wait and see the performance gains I got, after hoops and hooks with our beloved…

Chat-GPT: endless source of (ridiculous) solutions!

I had several theories on how to approach the problem, and as is usual for a noob like me, I went with the most complicated/stupid approaches first…

At one time for example, I had row numbers written within the raw data, just to be able to find them when the object was “flattened” to the actual fields I needed, so I had to slice out the row number before writing… 🥴

Although performance was not so bad (was still much better due to I/O reduction), it was such a shameful repetition: I already had the row numbers from the first division of new VS existing contacts, so why again?

The problem looked “geometrically” as follows: taking data from a 25-rows 2D array with row numbers, and use it to replace 25 rows within another 2D array of up to 3000 rows, without obvious row numbers… (Obvious 😏)

Asking solutions to Chat-GPT, I got answers that were incredibly weird…Here is a first reply about how to find the start and end row in the first place, which was suggested as a giant array reducing.

I thought it was a waste, and though I couldn’t pinpoint why right away, I proposed this ONE-LINE solution to ChatGPT, asking if it wasn’t better.

ChatGPT admitted it was better, but it noted it could have been slower or more memory consuming for large arrays (25 rows definitely isn’t big.)

Now I wanted to avoid a find on each iteration, although ChatGPT didn’t find it problematic. When prompted, it suggested a lookup object like this:

While it may seem more “elegant”, I still felt like both me and the AI were overlooking a MAJOR way of simplifying the whole problem.
It wasn’t about wrong answers, but wrong questions.

The right question was: why mapping 1000s of rows to change 25, instead of going through the 25, and updating only those? Execution time was ok, but my frugal 90s Computing Mindset ™️ was SHOUTING it was a waste! 😣

I was slowly catching up to the solution, but the AI didn’t help: when I pointed the issue, it proposed a puzzling double-counter tracking system…

Note the “assumption” it admits at the end: if that would be the case we wouldn’t have a problem, genius! 😂

Here it dawned on me: I already had a list of row numbers mapped in the same order as the data source: what stopped me from just changing the entries from the source, and then writing the updated array?

Nothing! Just my slow mind, and an AI that can’t think creatively!

So here’s the super-simple solution I should have thought before…

The trick was once again to think about array positions: since I already had an array of row numbers in the same order as the incoming contacts (created to find the first row to update, as seen above), all I needed was to subtract the value of firstRow from each row, and I had the position in the slice of sheet I was considering, starting from 0 as it should be! 😅

Now using this it takes around 5 seconds to do everything, while before it could be between 12 and 25. Saving up to 80% of the time is BIG! 🥳

The main loop, with my long lines, comments, and all… Hope it’s readable! 😅

Conclusions: we’re STILL SAFE from AI overlords! 😁

As stated by many others, including this game developer who wrote of a much more interesting (and complicated) scenarioChatGPT simply can’t replace people thinking creativelynot even in its vaunted version 4.

And there was not much creativity involved here: it was actually the kind of stuff I expected the AI to shine on, so just imagine the mess on real issues!

Plus, as stated by the blogger above, another point to consider is how much time one can LOSE by using Chat-GPT: we will want to try the solutions it proposes, and more often than not it will result in wild goose chases..! 🦆

Conclusion: use Chat-GPT, but only for very, very simple tasks, asking extremely clear and limited code snippets, just to speed up your writing.

But when it comes to real problem solving: DON’T EVEN! 😂

Delve into the rest of my coding journey at FusionWorks, in the articles below!

  • Recruiter Codes Chrome Extension: pt.1pt. 2 (Google Chrome TypeScript)
  • Tech Recruiter Tries Coding: pt.1pt.2pt.3pt.4 (Google Apps TypeScript)