I had just settled down to write some ScalaCheck tests when suddenly my peace and harmony was shattered by the news that the “download PDF” functionality on one of our sites had recently ceased functioning, and it would be quite nice if it could be restored to good working order. And so begins our tale.
Continue reading “On jspawnhelper and automatic updates”Month: November 2024
Pair programming with Cursor: A modern twist on collaborative coding
Imagine having a coding partner available 24/7—tireless, fast, and, while not flawless, always ready to help. With AI tools like Cursor, this vision of “pair programming” is now closer to reality, offering a fresh approach for solo developers and teams alike.
While AI development tools are becoming increasingly widespread, software developers often remain sceptical: Can an AI really help us write high-quality code? From my experience, using AI-powered tools reminds me of pair programming with a human. If you are reluctant to give them a go, I think approaching these tools from this perspective may help overcome this barrier, and ultimately will help you get the most out of them.
Why try AI pair programming?
Traditional pair programming has long been praised for its impact on code quality, error reduction, and team culture. Two developers work together at the same workstation, with one acting as the “driver” who writes code and the other as the “observer” who reviews each line. While this approach fosters learning and collaboration, it also requires two people on one task, which can be costly in terms of time and resources.
AI tools offer a practical, low-cost alternative to traditional pair programming. It provides instant feedback, useful suggestions, and a second set of “eyes” on your code—all without needing a human counterpart. While AI isn’t perfect, the back-and-forth interaction can feel surprisingly similar to a human pairing session, and it is beneficial to treat it this way.
How Cursor mimics traditional pair programming
The workflow will feel familiar to anyone who’s done pair programming before. Here’s how it typically unfolds:
- Starting the Task: You prompt Cursor to tackle a specific coding problem—perhaps generating a new function, designing a user interface, or drafting test cases.
- Iterative Refinement: Cursor generates an initial solution, which you review and adjust. You may request changes, fixes, or refactoring to refine its response.
- Moving Towards a Solution: Through this iterative feedback loop, Cursor refines the code until you reach a satisfactory result.
This conversational back-and-forth mimics the dynamic of traditional pair programming. Approaching development tasks in this way encourages you to think about what you are really trying to achieve, rather than getting sucked into the details of the implementation at the outset.
Using cursor as your code reviewer
One powerful feature of Cursor is its ability to act as a reviewer, in a sense reversing this workflow. Rather than asking Cursor to write code from scratch, you can ask it to review code you’ve written, offering suggestions for improvement or optimization. This can be particularly helpful when tackling coding tasks you are less comfortable with, such as frontend development or infrastructure as code. The AI can often spot potential improvements you might overlook, augmenting your knowledge in these areas.
This reverse workflow can feel like having an unbiased reviewer who you can call on without worrying about taking up their time. Cursor may suggest alternative approaches, flag potential pitfalls, or highlight areas for optimisation— if you ask.
An example: Building a search interface for a GraphRAG API
To illustrate how Cursor works in practice, let’s look at a recent project where I used it to build a simple front-end for a GraphRAG search API. The goal was to create an interface that could display the search responses, and pull out references to supporting documents. Here’s a step-by-step breakdown of how I approached the task:
- Defining the Extraction Task: The tricky part of this task was writing a set of regular expressions to identify and extract references to documents embedded in the API’s text responses. I started by prompting Cursor to parse the response, giving it a concrete example of the data I wanted to isolate. For instance, in a response snippet like:
“… as a result, Nvidia stocks are set to rise [Data: Reports (328); Entities (2542, 431); Relationships (372, 5709)]”
I explained that I needed to capture the document references—like “Reports (328)” or “Entities (2542, 431)” from the text.
- Testing with Cursor’s Help: Having generated an initial implementation, we worked together to write a set of tests. These tests were essential to validate that the references were extracted correctly, capturing different formats that might show up in the API’s responses.
- Reviewing and Identifying Edge Cases: I reviewed the extraction results on a wider set of data to identify any edge cases that the initial implementation had missed.
- Refining the Solution: I added these edge cases as additional test cases, and asked Cursor to amend the code to account for the variations. With each iteration, it refined the parser incrementally to handle the new scenarios.
- Removing duplication: As the test suite grew, Cursor was able to make suggestions to simplify the tests and remove duplicated code.
- Finalising the Implementation: Once the solution was passing all tests, I committed the code. With the references reliably extracted, I moved on to the next step: displaying the referenced data in the frontend. “Cursor, please display the references as cards below the search result …”
You can see in this example the natural back-and-forth process with Cursor, similar to pair programming with a human partner. By iteratively refining the regular expressions, testing thoroughly, and addressing edge cases, my AI partner helped turn a tricky data extraction task into a manageable and enjoyable workflow.
Benefits of AI pair programming
AI pair programming offers many of the same benefits as a human partner in pair programming. For example:
- Conversational Flow: Just as you’d ask a human partner for feedback, you interact through questions, prompts, and iterative requests, creating a “conversation” with your AI partner.
- AI as the Driver: The AI partner generates solutions based on your prompts, while you guide it toward the right path.
- Expecting Mistakes: Like a human partner, the AI will make mistakes. Together, you can refine and improve the code, iterating to align its output with your vision.
- Incremental Development: Working with AI encourages an incremental approach. You can request small sections of code, review each piece, and adjust as needed—fostering a step-by-step workflow that ensures quality.
- Broader Perspective: With the AI handling the details, you’re free to step back and consider larger concerns, like UX, accessibility, and project-specific requirements.
Key Differences from Human Pair Programming
While developing with an AI tool shares many of the benefits of traditional pair programming, there are also significant differences:
- Infinite Patience and Subservience: Unlike a human partner, the AI is endlessly patient and will take a backseat. You can ignore its suggestions without worrying about offence, conflict or having to take a long walk.
- Freedom from Judgment: There’s no fear of embarrassment when asking a question that might seem “basic.” The assistance is provided without judgement, creating a comfortable environment to explore, learn, and iterate.
- Knowledge Sharing and Mentorship: AI tools won’t spontaneously take you on a deep dive into the codebase, and lack the mentorship qualities that a human partner might offer. The explanations are often only as detailed as your requests.
- Code Ownership and Accountability: When pairing with a human, ownership of the code is shared. With an AI partner, the responsibility for quality and accuracy ultimately remains with you. It’s important that these tools are used within a robust development process, with peer code review and testing in a CI pipeline.
- Emotional Support and Empathy: A human partner can recognize signs of frustration, offer encouragement, or provide a sense of camaraderie that reduces burnout. An AI, whilst supportive in its own way, doesn’t provide emotional support 🙂.
Tips for effective AI pair programming with Cursor
Here are some best practices to help you get the most out of Cursor as your AI pair programming partner:
- Use Precise Prompts: The clearer your requests, the more accurate Cursor’s responses. Providing relevant context from your codebase use the files and folders commands helps Cursor generate output aligned with your goals.
- Handle Mistakes as Learning Moments: Cursor will make mistakes or miss the mark. Treat these errors as opportunities to refine your prompts and learn how best to interact with the AI.
- Experiment with the Reverse Workflow: If you’re unsure about a piece of code, ask Cursor to review it. Its suggestions can help you catch issues early and improve overall code quality.
- Know When Not to Use Cursor: Cursor may not be ideal for highly creative problem-solving or tasks that require complex decision-making. Use it as a support tool, but don’t rely on it for aspects that benefit from nuanced human judgement.
Final thoughts: Embracing AI as a pair programming partner
If you’re hesitant about using an AI-powered developer tool, thinking of it as a virtual pair programming partner may just be what you need to get started. Remember, these tools are in their infancy, and are continually improving—just like all good developers. Embrace the imperfections and cut them some slack; after all, you would not expect perfection of your partner, or yourself. Like it or not AI is here to stay and is a tool every professional developer needs to master.