In an earlier article (How to build a Chrome Browser Extension in 15 sentences using Windsurf) I related how I needed only 15 simple sentences to get the Cascade agent in the Windsurf IDE to produce a fully functional Chrome Browser extension that allows users to upload files from their file system to Oracle Cloud. The application is documented with an instructive README.md file and it is published in a GitHub Repository. It took me some 25 minutes to produce all of that (well, to get Windsurf to produce all of that).
In this article I will show the next steps we took — me and my buddy Cascade. Our little team effort has evolved from my giving commands and Cascade carrying them out to a collaboration where I take the initiative and subsequently we exchange ideas, I get options proposed and choose the ones I like and I also interrupt the agent to change course or redo its work in a (slightly) different manner. I have had to edit one line of code — where it seemed Cascade got stuck in stubborn misunderstanding.
This article discusses a number of aspects of the cocreation process that resulted in a more elaborate browser extension that allows not just file upload but also file browsing and file download (from the Oracle Cloud Object Storage). The extension allows me to right click any file link and any image in any web page in my browser and have it (file or image) directly uploaded to Oracle Cloud.
All code as well as the Readme and Architecture documentation is in this GitHub repo.
A short demo of the extension
Of course that same file is now also in OCI Object Storage:
Here is a quick snapshot of the Windsurf IDE as I am collaborating to create my/our application:
Collaborative Conversation Highlights
Some of the highlights I want to share:
- Cascade created a great architecture document to explain the structure of the application and also visualize it with two Mermaid diagrams
- I showed Cascade two images: one a sketch of the UI I had in mind (which it interpreted mostly right and immediately turned into working code for browsing files and folders) and one a screenshot with two visual flaws highlighted in red rectangles on the image; Cascade understood what was wrong and fixed the code!
- When I asked Cascade to bring the coloring scheme more in line with Oracle (red), it quickly adapted the look and feel to something that looks better than I could have designed, let alone implemented
- Cascade had somehow decided on the format of the JSON returned from Oracle Cloud with the list of objects in the bucket. I am not sure how it had made that assumption, but it was wrong. Here I had to step in and instruct it on the correct format; it immediately understood the information and applied it:
- The biggest change I made Cascade make was to add the ability to right click an image in a web page and have that image uploaded to Oracle Cloud. This change requires a thorough understanding of how in a browser extension a content script, a background script (service worker), context menu options and possibly the sidepanel work together, largely via asynchronous messages. Just a very conscise description was all it took:
- However, it did embark on a very complex implementation, where the content of the image was retrieved from the content script running in the current web page. A simpler approach is to simply pass the URL from the web page to the background service worker, as I suggested to Cascade
(however as I am typing this I realize that this works for publicly accessible images — but not for images I can only see in the context of the web page because of the authentication/authorization I have in the web page; instead I should have the content script inject a JavaScript function into the current webpage, have that function read the image into a blob and pass that to content, background and side panel; something for later)
- I have a brainwave about a major new feature: logical delete. Cascade embraces it, adds a suggestion for a toggle that allows the user to show and hide logically deleted files and eagerly starts coding:
It goes on to implement the feature, providing a great UX and apparent success. However, the logic is flawed. It is not able to know which files to hide in the UI. After many increasingly grumpy messages from my side, I have to step in and provide stricter guidance:
A few minutes later it returns to present its work:
And now the delete function is working as intended.
The documentation needs to be updated — and I have to tell Cascade about it. It readily complies:
One final thing with the deleted files — the index file of deleted files is visible to the user and can be deleted; Cascade understands the issue and resolves it in an elegant way:
- When it is time to refactor, Cascade has my back. I ask it for suggestions regarding the overly large sidepanel.js file. It presents a plan for reorganizing the code:
I consider this a good proposal and let Cascade get started on it:
- Implementing automated testing of the extension turns out to be quickly taken care of. Cascade is flexible enough to veer from its suggested Puppeteer testing tool to my preferred Playwright:
When I tried to run these tests, things were not as simple as they seemed. I got instructions from Cascade so that was good — although I also need to feed Cascade some information about what I saw going on.
- I realized I had not yet added unit testing; that was easily remedied. One question to my pal and she/he made sure to add these tests:
Note: it turned out later that these tests did not run. The test code was okay but the configuration of Jest was not. It took a while to get this sorted!
I am not fully expert in the Jest testing framework. I spot a line I cannot immediately fathom. Cascade can quickly help me understand the code:
I decide to intervene a little with the test cases, and ask Cascade for a specific element of mock data:
It complies of course and adds a little joke of its own:
When I tried to run the unit tests, I ran into an error:
And this started a series of attempts by Cascade to fix things. And it seemed to run into a rabbit hole where it could not easily recover itself.
Note that I had to point out that the test code created by Cascade was not actually testing the application at all, but only the mock it had generated for the test. Even though Cascade is pretty amazing and enormously powerful, you need to pay close attention.
I got very frustrated with Cascade and the way it could not make Jest work with Modules. I ended up asking ChatGPT for its help and I fed the results to Cascade.
This turned out not be enough. I had to create a simple example of a file importing a function from a module importing a function from a module (helped again by ChatGPT) and have Cascade use it as a template. Even then, it was incorrect in the path structure for module import and module mocking using jest.Mock(). It kept on creating stand alone tests (that did not import the module to test but included copied code). I have used all caps instructions. And I have used some expletives with the incredibly stubborn behavior of my intern. This would have been a very positive story if not for hunt for tests that were real! In hindsight, I should have enlisted ChatGPT earlier on, I should have gotten more engaged myself and I should have taken the time to create one functioning example myself. I relied for too long on Cascade’s ability to save the day itself. It could not.
- Funny how I can have Cascade amend a commit message:
- I wondered if we can do efficient problem analysis and bug fixing. I introduced a deliberate issue:
Then I verified that it was indeed causing wrong behavior — and it is:
Then I asked Cascade about the erroneous behavior I was now seeing:
and it could very quickly find and correct the issue — although it created code that is not super clean.
Conclusions
Some findings after two days of Windsurfing.
I had an enjoyable experience. I did not know how this way of application development would work for me. Not typing the programs myself, not being intimate with the code — would I like that? Talking to a bot — would that feel okay? As it turns out: yes. I like the speed with which I achieve results. I like the way in which I can work with Cascade. It really feels like a friendly, attentive, well behaved, listening, quite smart junior colleague who I can ask to perform chores and increasingly challenging, composite tasks. Instructing on the objectives, less on specific actions. Declarative rather than imperative.
I am delighted by the quality of code, organization, documentation and tests. It is far better than my code normally is. The documentation is clear, the visualization in the architecture diagram is really good. Being able to rapidly update documentation after implementing a feature is quite satisfying , as it gives some form of closure (as does a commit & push). The help I get with git-interaction is also nice. I am no hero with git commands, and Cascade helps me navigate these nicely.
Having Cascade help me understand the code is working great. Instead of bothering my colleagues over and over with small, stupid questions or instead not bothering them and endlessly browsing the internet hoping to find the explanation I need, I can ask my Cascade comrade — endlessly if I like — and have them make it all clear.
Refactoring the code was a breeze: having Cascade come up with a proposal and then have it break out the modules from sidepanel.js and tie all pieces together with exported and local functions — that was really good. Something I would normally be reluctant about and perhaps even fear has now become an obvious thing to do. I would almost expect Cascade to suggest doing this instead of waiting for me to bring it up. Maybe I should discuss this in our sprint retrospective.
There were some instances where Cascade did not resolve challenges. It struggled with the concept of virtual folders in Oracle Cloud object storage (there are no folders but a segment of “<name>/” is interpreted as a folder name. It took some time for Cascade to understand this (and for me to explain it properly) and this was the only time I stepped in and made manual code change. Cascade could not get it right. It assumed the JSON structure for Oracle Cloud’s list of objects; it assumed incorrectly and in hindsight should have verified this assumption with me. The solution for the recycle bin with logically deleted files did not work. I did not check Cascade’s code in detail, but it never worked. Only when I provided a simple (functional) approach did Cascade very quickly get it right.
It took a while to get the tests set up right. Even though it created the code, selected the test tool (Jest) and configured the package.json and then also created the tests, Cascade had a hard time being able to run them properly. Getting the right configuration, working correctly with ES Modules, configuring the correct packages — it took many iterations (and at one point Cascade seemed ready to give up entirely). In all fairness, after I told it not to quit but to fix its own mess, it resumed the error analysis andfinally got it to work. After I told it to, it even updated the Readme document with instructions on how to run the tests (initially forgetting the unit tests).
There were times when I felt like banging my head against the wall as Cascade kept on repeating the same bad behavior it had promised me it would not engage in again. It got distracted from the objective we were working on, went for a “simpler” approach that simply did not achieve the same objective and was not able to go out there (to the internet, to ChatGPT, to StackOverflow) to get unstuck. I need to be aware of these situations and then intervene myself — sooner than I did this time.
There where some funny, silly episodes in all this, especially when Cascade admitted that one of its attempts had as a drawback “Cons: Not testing the actual code that will run in production” (it only tested the mock). Only after I explicitly told Cascade to come out of the hole, remove all its earlier attempts at creating tests and start all over again did it achieve the desired result. Not unlike how I would have gone about this: first trying harder and harder, digging in ever deeper, to get it to work before finally completely giving up and starting over (with a somewhat less ambitious first few steps)
Am I still in touch with the code base? I would have to admit that I am not [as much as I probably should be]. I marvel at all the artefacts. I am happy the application does what I want it to do. I have built similar applications before, which means I can understand reasonably well what Cascade has produced (for example the configuration and structure of the browser extension and the interaction with Oracle Cloud). Without that prior experience, I would not be able to grasp easily — without guidance — how that works and therefore why my code works. I would have to rely very rapidly on Cascade, other tools or experienced colleagues to continue development of this application.