The other week I posted about best practices and pro tips on creating prompts with Cursor. At the heart of these tips was the idea of providing the composer as much context as possible.
In Cursor you could this through two primary ways:
- By @mentioning docs or code directly into the composer window or via Cursor settings
- Generally by creating more prescriptive prompts such as “always use .env files for storing credentials”
If there is anything you get used to working in tech, and especially now with AI, is the rapid pace of change.
Since I wrote that post, a LOT has changed. Tools like Cursor and Windsurf have introduced rules files. Rules files, and understanding which LLM are best for what task, are a game changer for how I’ve been working with prompts and coding.
You can think of rules files are a place where you can specify longer, more prescriptive rules that your prompts in your project must follow. If you are used to working with dependency files in coding, it’s kind of like this but on steroids.
At the same time, we’ve moved from generative AI assistants to collaborative agentic AI.
Put these together, and the results are pretty amazing!
Here’s an example.
Let’s say I’m using Cursor to build a bunch of data apps and using Airbyte as the data movement platform and Streamlit for the frontend. I’m writing in Python and using the Airbyte API libraries. This is my basic ‘tech stack’.
I can define this in my .cursorrules
file that I put in the root of my project folder.
- name: "Import Required Libraries"
action: "Always ensure the required libraries are imported at the top of the file."
details:
- "os: To load environment variables."
- "dotenv: To load .env file for credentials."
- "pandas: For mapping API results to DataFrame."
- "airbyte_api_client: For interacting with Airbyte APIs."
- "airbyte_api_client.api: Import API-specific modules."
- "airbyte_api_client.configuration: To configure the API client."
- "requests: For token management and refreshing the token."
I can go further as well and tell Cursor some of my coding preferences, such as how I want to authenticate, store sensitive keys, and how to work with data.
- name: "Store Sensitive Information Securely"
action: "All API keys, tokens, client IDs, and client secrets must be stored in a .env file."
details:
- ".env file example:"
content: |
AIRBYTE_API_URL=https://your-airbyte-instance.com
AIRBYTE_CLIENT_ID=your_client_id
AIRBYTE_CLIENT_SECRET=your_client_secret
- "Load credentials using the python-dotenv library."
- "Example code:"
content: |
from dotenv import load_dotenv
import os
load_dotenv()
AIRBYTE_API_URL = os.getenv('AIRBYTE_API_URL')
AIRBYTE_CLIENT_ID = os.getenv('AIRBYTE_CLIENT_ID')
AIRBYTE_CLIENT_SECRET = os.getenv('AIRBYTE_CLIENT_SECRET')
- name: "Authenticate Using Bearer Token"
action: "Use client and secret keys to fetch a new bearer token before making API requests."
details:
- "Add token refresh logic in a reusable function."
- "Example function to get a fresh token:"
content: |
import requests
def get_access_token():
"""
Fetch a new access token using client ID and secret.
Returns:
str: Bearer token.
"""
url = f"{AIRBYTE_API_URL}/oauth/token"
payload = {
"client_id": AIRBYTE_CLIENT_ID,
"client_secret": AIRBYTE_CLIENT_SECRET,
"grant_type": "client_credentials"
}
response = requests.post(url, json=payload)
response.raise_for_status()
return response.json()["access_token"]
Here’s where things really get interesting. I want my code to be easy to maintain and well structured. Early genAI assistants really struggled here. They often generated monolithic blocks of code, that whilst they worked, they were pretty ugly. I want my rules to tell Cursor how I like my code. Thankfully, agentic AI can use reasoning to figure out how to create well structured code. In the world of Python, PEP 8 provides a style guide for best practices on how to write good code. I can now tell Cursor to use this guide whenever it writes code!
- name: "Follow PEP8 Naming Conventions"
action: "Ensure that variable, function, and class names adhere to PEP8 standards."
details:
- "Use snake_case for variable and function names."
- "Use PascalCase for class names."
- "Limit line length to 79 characters."
- "Prefix all private variables and functions with an underscore."
- "Use encapsulation for best practices"
Now, when I create rules, or generate code, my functions are neat, easy to read, and practices good encapsulation technics. All the things I would -- well, should -- do when coding.
- name: "Map Results to pandas DataFrame"
action: "Always convert API response data to pandas DataFrame for further processing."
details:
- "Example function to convert API response to DataFrame:"
content: |
import pandas as pd
def map_to_dataframe(data, columns=None):
"""
Convert API response data to pandas DataFrame.
Args:
data (list or dict): JSON response data.
columns (list, optional): List of column names to include.
Returns:
pd.DataFrame: Data in tabular format.
"""
df = pd.DataFrame(data)
if columns:
df = df[columns]
return df
PEP 8, also offers guidelines for documenting code, how to handle error messages, and much more. I can add examples in my .cursorrules
file that problems can use as context whenever creating new code.
- name: "Error Handling for API Requests"
action: "Implement robust error handling for API calls."
details:
- "Use try-except blocks to handle common exceptions."
- "Raise errors for debugging when necessary."
- "Example:"
content: |
try:
api_client = get_api_client()
df = get_workspaces(api_client)
print(df.head())
except Exception as e:
print(f"An error occurred: {e}")
Putting it all together, my current .cursorrules
file for Airbyte development in Python means I can spend more time thinking about the task I want the prompt to perform vs. how to structure the actual prompt to create best practice code. This has saved me hours and hours of refactoring time.
And, since I switch between Python, React, and Swift on a regular basis, I’ve started creating rules files for each language. This approach let’s me apply best practices per language, define things like directory structures in my projects and much more. If you want to check out my rules files, I’ve added them to a GitHub repo. I'm constantly refactoring and adding more. Star and follow along if you want to stay up to date.
# CursorRules file for Xcode Swift Project
# Defines best practices for directory structure
# Base directory
ProjectRoot:
- README.md # Project documentation
- LICENSE # License file
- .gitignore # Git ignore rules
- .xcodeproj # Xcode project file
- .xcworkspace # Xcode workspace file (if using CocoaPods or similar)
# Primary folders
Sources: # All source code files
- App: # Main app folder
- Models: # Data models
- Views: # User interface components
- ViewModels: # Logic connecting views and models (MVVM pattern)
- Controllers: # View controllers (MVC pattern)
- Resources: # Assets and other resources
- Assets.xcassets # Image and color assets
- LaunchScreen.storyboard # Launch screen storyboard
- Localizations: # Localized strings and assets
Tests: # Unit and UI tests
- UnitTests: # Unit test cases
- UITests: # UI test cases
# Supporting files
Configurations: # Build and app configurations
- Debug.xcconfig # Debug configuration settings
- Release.xcconfig # Release configuration settings
Extensions: # Code extensions (e.g., for UIKit or SwiftUI)
- String+Extensions.swift
- Date+Extensions.swift
Utilities: # General utilities and helpers
- NetworkManager.swift
- APIConstants.swift
# Example
Example: # Example apps or demos
- ExampleApp: # Example implementation using the project
Documentation: # Developer documentation
- Docs.md # Project documentation
- Tutorials: # Tutorials for using the app
# Notes
- Organize files logically into respective folders based on MVC or MVVM architecture.
- Ensure all resources are in `Resources`.
- Follow naming conventions and avoid file duplication.
- Unit tests mirror the structure of the `Sources` folder.
But, back to my Airbyte + Streamlit app. Now that I have my rules file created, all I need to do is write a simple prompt and let AI do the work
create a streamlit app that connects to my airbyte cloud workspace and
lists available connections, and let's me click on them to show the stream available.
As you can see, the code gets generated, and composer gives me a run down of the rules that it has followed to create it. I honestly don't know how I ever lived without it. Rules files are a game changer for developers and prompts.
Top comments (0)