Cursor AI and My Coding Workflow

Hey everyone, Hugo here!

As a developer at Money Forward, I’m always looking for ways to optimize my workflow and become more efficient. Recently, I stumbled upon Cursor AI, and let me tell you, it’s been a total game-changer. I wanted to share my experience and dive into the technical aspects of how it’s impacted my daily work.

Before Cursor AI, I often felt bogged down. Debugging those gnarly, edge-case bugs? Hours. Setting up a boilerplate for new features? Tedious. Keeping up with the latest best practices and framework updates? Exhausting. I’d find myself constantly switching between my IDE, documentation, and Stack Overflow, trying to piece everything together. It felt like my productivity was being chipped away, bit by bit.

Example: Write a method to count the number of empty seats on an airplane

class Airplane
  def initialize(total_seats)
    @seats = Array.new(total_seats, nil)  # Initialize all seats as empty
  end
  
  def count_empty_seats
    empty_seats = 0
    @seats.each do |seat|
      empty_seats += 1 if seat.nil?
    end
    empty_seats
  end
  
  def assign_passenger(seat_number, passenger_name)
    @seats[seat_number] = passenger_name if seat_number < @seats.length
  end
end
# Example usage:
airplane = Airplane.new(10)  # Create an airplane with 10 seats
airplane.assign_passenger(0, "John")
airplane.assign_passenger(2, "Mary")
puts "Number of empty seats: #{airplane.count_empty_seats}"  
# Output: Number of empty seats: 8

This approach uses straightforward logic that’s easy to follow and doesn’t involve advanced Ruby features, design patterns, or complex structures.

After applying AI suggestions, we see improvements such as:

  • Applying SOLID principles
  • Leveraging Ruby-specific features
  • Adding proper error handling
  • Ensuring data consistency (e.g., seat count starting from 1)

The resulting code structure is as follows:

require 'forwardable'
module Airline
  class Seat
    attr_reader :number, :passenger
    def initialize(number)
      @number = number
      @passenger = nil
    end
    def assign_passenger(passenger_name)
      raise ArgumentError, 'Passenger name cannot be empty' if passenger_name.to_s.strip.empty?
      raise SeatError, 'Seat is already occupied' if occupied?
      @passenger = passenger_name
      self
    end
    def empty?
      @passenger.nil?
    end
    def occupied?
      !empty?
    end
    def to_s
      "Seat #{number}: #{occupied? ? passenger : 'Empty'}"
    end
  end
  class Airplane
    extend Forwardable
    def_delegators :@seats, :size, :each, :map
    class SeatError < StandardError; end
    def initialize(total_seats)
      raise ArgumentError, 'Total seats must be positive' unless total_seats.positive?
      @seats = (1..total_seats).map { |number| Seat.new(number) }
    end
    def count_empty_seats
      @seats.count(&:empty?)
    end
    def count_occupied_seats
      @seats.count(&:occupied?)
    end
    def assign_passenger(seat_number, passenger_name)
      seat = find_seat(seat_number)
      seat.assign_passenger(passenger_name)
    rescue SeatError => e
      handle_seat_error(e, seat_number)
    end
    def seat_status(seat_number)
      find_seat(seat_number).to_s
    end
    def available_seats
      @seats.select(&:empty?).map(&:number)
    end
    def occupied_seats
      @seats.select(&:occupied?).map { |seat| [seat.number, seat.passenger] }.to_h
    end
    def to_s
      "Airplane with #{size} seats (#{count_empty_seats} empty, #{count_occupied_seats} occupied)"
    end
    private
    def find_seat(seat_number)
      @seats[seat_number - 1] || raise(SeatError, "Invalid seat number: #{seat_number}")
    end
    def handle_seat_error(error, seat_number)
      case error
      when SeatError
        puts "Error assigning seat #{seat_number}: #{error.message}"
      else
        raise error
      end
    end
  end
end
  • Usage:
begin
  # Create an airplane with 10 seats
  airplane = Airline::Airplane.new(10)
  
  # Assign some passengers
  airplane.assign_passenger(1, "John Doe")
  airplane.assign_passenger(3, "Jane Smith")
  
  # Get various information
  puts airplane.to_s
  puts "\nAvailable seats: #{airplane.available_seats.join(', ')}"
  puts "\nOccupied seats:"
  airplane.occupied_seats.each do |seat_number, passenger|
    puts "  #{seat_number}: #{passenger}"
  end
  
  # Try to assign to an occupied seat
  airplane.assign_passenger(1, "Bob Wilson")
  
  # Try to assign to an invalid seat
  airplane.assign_passenger(11, "Invalid Seat")
rescue StandardError => e
  puts "Error: #{e.message}"
end

The code was generated by Cusor AI

This implementation is more robust, maintainable, and follows Ruby best practices. It’s suitable for a production environment and can be easily extended with additional features like:

  • Seat classes (economy, business, first)
  • Passenger information management
  • Flight scheduling
  • Seat pricing
  • Reservation system

The code is also improved in the following areas:

  • Better testability
  • Follows the principle of least surprise
  • Enhanced readability and maintainability

The Moment of Discovery: Diving into Cursor AI

I remember seeing Cursor AI mentioned in a developer forum. I was skeptical at first. Another AI tool making big promises? I’d seen plenty of those. But something about it piqued my interest, and I decided to take the plunge. I downloaded it, integrated it with my VS Code setup, and started experimenting.

Logo of Cursor AI(Source: https://cursor.com/en-US/brand)

1. Code Quality: From Buggy to Bulletproof

Cursor AI’s real-time code analysis is incredible. It catches syntax errors, suggests optimizations, and even points out potential security vulnerabilities I might have missed. It’s like having a senior developer constantly doing code reviews alongside me. I’ve noticed a significant drop in the number of runtime errors and a substantial improvement in the maintainability of my code.

Before:

def calculate_total
  sum = 0
  items.each do |item|
    sum += item.price * item.quantity
  end
  sum
end

After (with Cursor AI’s suggestions):

def calculate_total
  items.sum { |item| item.price * item.quantity }
end

2. Time Savings: Automating the Tedious Tasks

Generating test stubs, writing API documentation, and refactoring large blocks of code—these tasks used to eat up a huge chunk of my time. Now, Cursor AI handles a lot of this. For example, I can ask it to “generate unit tests for this function,” and it provides a solid starting point. This has freed up my time to focus on more complex logic and problem-solving.

  • Example: Method to find the maximum number in an array
# The method finds max number in array
def find_max(arr)
  arr.max
end
# The method finds max number in array
def find_max(arr)
  arr.max
end
  • The unit test that Cursor AI suggests:
require 'rspec'
# Test suite
RSpec.describe 'find_max' do
  it 'returns the maximum for typical cases' do
    expect(find_max([1, 2, 3])).to eq(3)
    expect(find_max([-1, -5, -2])).to eq(-1)
  end
  it 'handles duplicate and float values' do
    expect(find_max([5, 5, 3])).to eq(5)
    expect(find_max([1.5, 2.7, 2.9])).to eq(2.9)
  end
  it 'raises an error for invalid input' do
    expect { find_max([]) }.to raise_error(NoMethodError)
    expect { find_max(nil) }.to raise_error(NoMethodError)
  end
end

3. Productivity Boost: Navigating with Ease

The AI-powered code navigation is stellar. I can quickly jump to function definitions, find related code, and understand the dependencies between different parts of my project. This has significantly reduced the time I spend just trying to understand the codebase. Plus, the smart code suggestions have helped me explore new patterns and techniques, pushing me beyond my usual coding habits.

  • Example: ShoppingCart
class ShoppingCart
  attr_reader :items, :total
  def initialize
    @items = []
    @total = 0
  end
  # Add an item to the cart
  def add_item(item)
    @items << item
    calculate_total
  end
  # Remove an item from the cart
  def remove_item(item)
    @items.delete(item)
    calculate_total
  end
  # Calculate the total price
  def calculate_total
    @total = @items.sum { |item| item[:price] }
  end
  # Apply a discount to the total
  def apply_discount(percentage)
    @total = @total * (1 - percentage / 100.0)
  end
end
  • Usage:
def run_example
  # Create a new shopping cart
  cart = ShoppingCart.new
  # Add some items
  cart.add_item({ name: "Apple", price: 1.99 })
  cart.add_item({ name: "Banana", price: 0.99 })
  cart.add_item({ name: "Orange", price: 1.49 })
  # Show initial total
  puts "Initial total: $#{cart.total}"
  # Apply a 10% discount
  cart.apply_discount(10)
  puts "Total after 10% discount: $#{cart.total}"
  # Remove an item
  cart.remove_item({ name: "Banana", price: 0.99 })
  puts "Total after removing banana: $#{cart.total}"
end
# Run the example
run_example

4. Learning and Growth: Deepening My Knowledge

Cursor AI doesn’t just give me the answer; it often explains why it’s suggesting a particular solution. This has been invaluable for my learning. I’ve been able to dive deeper into the intricacies of the frameworks and languages I use. It’s like having a tutor on demand. I’ve discovered new features and best practices I wasn’t even aware of before.

  • Example: Student
class Student
  # Using attr_accessor instead of separate attr_reader and attr_writer
  # This is a Ruby best practice for simple getter/setter methods
  attr_accessor :name, :grades
  def initialize(name)
    @name = name
    @grades = []
  end
  # Method to add a grade with validation
  def add_grade(grade)
    return false unless grade.between?(0, 100)
    
    @grades << grade
    true
  end
  # Calculate average grade using Ruby's built-in methods
  def average_grade
    return 0 if @grades.empty?
    
    @grades.sum.to_f / @grades.length
  end
  # Get letter grade based on average
  def letter_grade
    case average_grade
    when 90..100 then 'A'
    when 80..89  then 'B'
    when 70..79  then 'C'
    when 60..69  then 'D'
    else 'F'
    end
  end
end
  • Usage:
def run_student_example
  # Create a new student
  student = Student.new("John Doe")
  # Add some grades
  puts "Adding grades..."
  student.add_grade(85)  # Valid grade
  student.add_grade(92)  # Valid grade
  student.add_grade(105) # Invalid grade (will be rejected)
  student.add_grade(78)  # Valid grade
  # Display results
  puts "\nStudent: #{student.name}"
  puts "Grades: #{student.grades.join(', ')}"
  puts "Average: #{student.average_grade.round(2)}"
  puts "Letter Grade: #{student.letter_grade}"
end
# Run the example
run_student_example

5. Collaboration: Enhancing Team Communication

I’ve found that my code reviews have become much more efficient. With Cursor AI helping me write clearer comments and generate more comprehensive documentation, my teammates can understand my code faster. This has led to more productive discussions and fewer misunderstandings.

Technical Recommendations and Thoughts

For developers who want to:

  • Improve code quality and reduce bugs
  • Automate tedious tasks and save time
  • Enhance code navigation and project understanding
  • Deepen their technical knowledge
  • Improve team collaboration

I strongly recommend giving Cursor AI a try. It’s been an invaluable tool in my technical arsenal, and I think it could be for you too. If you’re using VS Code, the integration is seamless.

Final Thoughts

Cursor AI has not only changed my workflow but also how I approach coding. I’m more confident, more efficient, and more excited about the work I do. It’s been a powerful ally in my journey as a developer, and I’m eager to see how it evolves in the future.

Published-date